package com.bringspring.system.message.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.servlet.ServletUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bringspring.common.base.Pagination;
import com.bringspring.common.base.UserInfo;
import com.bringspring.common.config.ConfigValueUtil;
import com.bringspring.common.constant.MessageTypeConst;
import com.bringspring.common.database.util.ConnUtil;
import com.bringspring.common.database.model.DataSourceModel;
import com.bringspring.common.model.OnlineUserModel;
import com.bringspring.common.model.OnlineUserProvider;
import com.bringspring.common.util.*;
import com.bringspring.common.util.context.SpringContext;
import com.bringspring.system.base.entity.MessageTemplateEntity;
import com.bringspring.system.message.entity.MessageEntity;
import com.bringspring.system.message.entity.MessageReceiveEntity;
import com.bringspring.system.message.mapper.MessageMapper;
import com.bringspring.system.message.service.MessageService;
import com.bringspring.system.message.service.MessagereceiveService;
import com.bringspring.system.message.util.SentMessageUtil;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.github.pagehelper.util.StringUtil;
import lombok.Cleanup;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.text.StringSubstitutor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.stream.Collectors;

/**
 * 消息实例
 *
 * @author 开发平台组
 * @version V1.0.0
 * @copyright 股份有限公司
 * @date 2017年9月27日 上午9:18
 */
@Service
@Slf4j
public class MessageServiceImpl extends ServiceImpl<MessageMapper, MessageEntity> implements MessageService {

    @Autowired
    private UserProvider userProvider;
    @Autowired
    private ConfigValueUtil configValueUtil;
    @Autowired
    private DataSourceModel dataSourceUtils;
    @Autowired
    private MessagereceiveService messagereceiveService;

    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private SentMessageUtil sentMessageUtil;

    @Override
    public List<MessageEntity> getNoticeList(Pagination pagination) {
        // 定义变量判断是否需要使用修改时间倒序
        boolean flag = false;
        QueryWrapper<MessageEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(MessageEntity::getType, 1);
        //关键词（消息标题）
        if (!StringUtils.isEmpty(pagination.getKeyword())) {
            flag = true;
            queryWrapper.lambda().like(MessageEntity::getTitle, pagination.getKeyword());
        }
        //默认排序
        queryWrapper.lambda().orderByAsc(MessageEntity::getEnabledMark).orderByDesc(MessageEntity::getCreatorTime);
        if (flag) {
            queryWrapper.lambda().orderByDesc(MessageEntity::getLastModifyTime);
        }
        queryWrapper.lambda().select(MessageEntity::getId, MessageEntity::getCreatorUserId, MessageEntity::getEnabledMark, MessageEntity::getLastModifyTime, MessageEntity::getTitle);
        Page<MessageEntity> page = new Page<>(pagination.getCurrentPage(), pagination.getPageSize());
        IPage<MessageEntity> iPage = this.page(page, queryWrapper);
        return pagination.setData(iPage.getRecords(), page.getTotal());
    }

    @Override
    public List<MessageEntity> getNoticeList() {
        QueryWrapper<MessageEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(MessageEntity::getType, 1).eq(MessageEntity::getEnabledMark, 1);
        queryWrapper.lambda().orderByAsc(MessageEntity::getSortCode);
        return this.baseMapper.selectList(queryWrapper);
    }

    @Override
    public List<MessageEntity> getDashboardNoticeList() {
        UserInfo userInfo = userProvider.get();
        QueryWrapper<MessageEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(MessageEntity::getType, 1).eq(MessageEntity::getEnabledMark, 1);
        queryWrapper.lambda().and(query->{
            query.like(MessageEntity::getToUserIds,userInfo.getUserId())
                    .or().eq(MessageEntity::getSendType,"user").eq(MessageEntity::getToUserIds,"");
        });
        queryWrapper.lambda().orderByAsc(MessageEntity::getSortCode).orderByDesc(MessageEntity::getCreatorTime);
        Page<MessageEntity> page = new Page<>(1, 20);
        IPage<MessageEntity> iPage = this.page(page, queryWrapper);
        return iPage.getRecords();
    }

    @Override
    public List<MessageEntity> getMessageList(Pagination pagination, String type,String appid,String isShowRead) {
        return getMessageList(pagination, type, null,appid,isShowRead);
    }

    @Override
    public List<MessageEntity> getMessageList(Pagination pagination, String type) {
        return getMessageList(pagination, type, null,null,"0");
    }

    @Override
    public List<MessageEntity> getMessageList(Pagination pagination, String type, String user,String appid,String isShowRead) {
        String userId = StringUtil.isEmpty(user) ? userProvider.get().getUserId() : user;
        Map<String, String> map = new HashMap<>(16);
        map.put("userId", userId);
        StringBuilder sql = new StringBuilder();
        //关键词（消息标题）
        if (!StringUtil.isEmpty(pagination.getKeyword())) {
            sql.append(" AND m.title like '%" + pagination.getKeyword() + "%' ");
        }
        //消息类别
        if (!StringUtil.isEmpty(type)) {
            if (type.contains(",")){
                sql.append(" AND m.type in (" + type + ")");
            }else{
                sql.append(" AND m.type = '" + type + "'");
            }
        }
//        sql.append(" and m.f_id in ('456375212577957381') ");
        // 查询还未阅读的消息
//        if (StrUtil.isNotEmpty(isShowRead) && StrUtil.equals(isShowRead,"1")){  //需要展示已读的
//kanakn1
//        }else {
        if(StrUtil.isNotEmpty(isShowRead)){
            QueryWrapper<MessageReceiveEntity> queryWrapper = new QueryWrapper<>();
            queryWrapper.lambda().eq(MessageReceiveEntity::getUserId,userId)
                    .eq(MessageReceiveEntity::getIsRead,isShowRead);
            List<MessageReceiveEntity> list = messagereceiveService.list(queryWrapper);
            String collect = list.stream().map(MessageReceiveEntity::getMessageId).collect(Collectors.joining(","));
            if (StringUtil.isEmpty(collect))
                return CollectionUtil.list(false);
            sql.append(" and m.id in ( ").append(collect).append(" )");
        }

//        }

        List<MessageTypeConst> allToList = MessageTypeConst.getAllToList();
        List<MessageTypeConst> collect = allToList.stream().filter(x -> {
            return String.valueOf(x.getType()).startsWith("2");
        }).collect(Collectors.toList());
        List<Integer> contractMessage = collect.stream().map(MessageTypeConst::getType).collect(Collectors.toList());
        if (StrUtil.isNotEmpty(appid)){
//           String collect2 = contractMessage.stream().map(String::valueOf).collect(Collectors.joining(","));
//           if (StrUtil.equals(appid,Constants.CONTRACT_APPID)){
//               sql.append(" and m.F_type in (").append(collect2).append(" ) ");
//           }else {
//               sql.append(" and m.F_type not in (").append(collect2).append(" ) ");
//           }
        }else {
            if(StringUtil.isNotEmpty( ServletUtils.getHeader("jsbos-origin"))){
                boolean pc = ServletUtils.getHeader("jsbos-origin").equals("pc");
                if (!pc){
                    contractMessage.add(4);
                }
            }
            String collect2 = contractMessage.stream().map(String::valueOf).collect(Collectors.joining(","));
            sql.append(" and m.type not in (").append(collect2).append(" ) ");
        }

        sql.append(" ORDER BY  creator_time desc");
        map.put("sql", sql.toString());
//        List<MessageEntity> lists = this.baseMapper.getMessageList(map);
//        return pagination.setData(PageUtil.getListPage((int) pagination.getCurrentPage(), (int) pagination.getPageSize(), lists), lists.size());
        PageHelper.startPage((int) pagination.getCurrentPage(), (int) pagination.getPageSize());
        List<MessageEntity> lists = this.baseMapper.getMessageList(map);
        PageInfo<MessageEntity> pageInfo = new PageInfo<>(lists);
        return pagination.setData(pageInfo.getList(), pageInfo.getTotal());
    }

    @Override
    public List<MessageEntity> getMessageList(Pagination pagination) {
        return this.getMessageList(pagination, null);
    }

    @Override
    public MessageEntity getinfo(String id) {
        QueryWrapper<MessageEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(MessageEntity::getId, id);
        return this.getOne(queryWrapper);
    }

    @Override
    public MessageEntity getInfoDefault(int type) {
        QueryWrapper<MessageEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(MessageEntity::getType, type).eq(MessageEntity::getEnabledMark, 1);
        if (type == 1) {
            queryWrapper.lambda().orderByDesc(MessageEntity::getCreatorTime);
        } else {
            queryWrapper.lambda().orderByDesc(MessageEntity::getLastModifyTime);
        }
        // 只查询id
        queryWrapper.lambda().select(MessageEntity::getId, MessageEntity::getTitle, MessageEntity::getCreatorTime);
        List<MessageEntity> list = this.page(new Page<>(1, 1, false), queryWrapper).getRecords();
        MessageEntity entity = new MessageEntity();
        if (list.size() > 0) {
            entity = list.get(0);
        }
        return entity;
    }

    @Override
    @DSTransactional
    public void delete(MessageEntity entity) {
        this.removeById(entity.getId());
        QueryWrapper<MessageReceiveEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(MessageReceiveEntity::getMessageId, entity.getId());
        messagereceiveService.remove(queryWrapper);
    }

    @Override
    public void create(MessageEntity entity) {
        entity.setId(RandomUtil.uuId());
        entity.setBodyText(entity.getBodyText());
        entity.setType(1);
        entity.setEnabledMark(0);
        entity.setCreatorUserId(userProvider.get().getUserId());
        this.save(entity);
    }

    @Override
    public boolean update(String id, MessageEntity entity) {
        entity.setId(id);
        entity.setBodyText(entity.getBodyText());
        entity.setCreatorUserId(userProvider.get().getUserId());
        return this.updateById(entity);
    }

    @Override
    public MessageReceiveEntity messageRead(String messageId) {
        String userId = userProvider.get().getUserId();
        QueryWrapper<MessageReceiveEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(MessageReceiveEntity::getUserId, userId).eq(MessageReceiveEntity::getMessageId, messageId);
        MessageReceiveEntity entity = messagereceiveService.getOne(queryWrapper);
        if (entity != null) {
            entity.setIsRead(1);
            entity.setReadCount(entity.getReadCount() == null ? 1 : entity.getReadCount() + 1);
            entity.setReadTime(new Date());
            messagereceiveService.updateById(entity);
        }
        return entity;
    }

    @Override
    @DSTransactional
    public void messageRead() {
        String userId = userProvider.get().getUserId();
        QueryWrapper<MessageReceiveEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(MessageReceiveEntity::getUserId, userId).eq(MessageReceiveEntity::getIsRead, 0);
        List<MessageReceiveEntity> entitys = messagereceiveService.list(queryWrapper);
        if (entitys.size() > 0) {
            for (MessageReceiveEntity entity : entitys) {
                entity.setIsRead(1);
                entity.setReadCount(entity.getReadCount() == null ? 1 : entity.getReadCount() + 1);
                entity.setReadTime(new Date());
                messagereceiveService.updateById(entity);
            }
        }
    }

    @Override
    @DSTransactional
    public void deleteRecord(List<String> messageIds) {
        String userId = userProvider.get().getUserId();
        QueryWrapper<MessageReceiveEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(MessageReceiveEntity::getUserId, userId).in(MessageReceiveEntity::getMessageId, messageIds);
        messagereceiveService.remove(queryWrapper);
    }

    @Override
    public int getUnreadCount(String userId) {
        QueryWrapper<MessageReceiveEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(MessageReceiveEntity::getUserId, userId).eq(MessageReceiveEntity::getIsRead, 0);
        return (int) messagereceiveService.count(queryWrapper);
    }

    @Override
    public int getUnreadNoticeCount(String userId) {
        int result = this.baseMapper.getUnreadNoticeCount(userId);
        return result;
    }

    @Override
    public int getUnreadMessageCount(String userId) {
        int result = this.baseMapper.getUnreadMessageCount(userId);
        return result;
    }
    @Override
    @DSTransactional
    public void sentNotice(List<String> toUserIds, MessageEntity entity) {
        UserInfo userInfo = userProvider.get();
        entity.setEnabledMark(1);
        entity.setLastModifyTime(new Date());
        entity.setLastModifyUserId(userInfo.getUserId());
        this.updateById(entity);
        // 存到redis中的key对象
        List<String> idList = new ArrayList<>();
        // 存到redis，生成Redis的key
        Callable executeInsert = () -> {
            try {
                executeInsert(toUserIds, idList);
            } catch (Exception e) {
                entity.setEnabledMark(0);
                entity.setLastModifyTime(new Date());
                entity.setLastModifyUserId(userInfo.getUserId());
                this.updateById(entity);
                return false;
            }
            return true;
        };
        ThreadPoolTaskExecutor threadPoolExecutor = SpringContext.getBean(ThreadPoolTaskExecutor.class);
        Future<Boolean> submit = threadPoolExecutor.submit(executeInsert);
        try {
            if (submit.get()) {
                // 执行发送公告操作
                Runnable runnable = () -> executeBatch(idList, entity, userInfo);
                threadPoolExecutor.submit(runnable);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }

    /**
     * 数据存到redis中
     *
     * @param toUserIds 接受者id
     */
    private void executeInsert(List<String> toUserIds, List<String> idList) throws Exception {
        List<String> key = new ArrayList<>();
        try {
            int frequency = 10000;
            int count = toUserIds.size() / frequency + 1;
            for (int i = 0; i < count; i++) {
                // 生成redis的key
                String cacheKey = RandomUtil.uuId() + toUserIds.get(i);
                // 存到redis
                int endSize = Math.min(((i + 1) * frequency), toUserIds.size());
                redisUtil.insert(cacheKey, toUserIds.subList(i * frequency, endSize));
                key.add(cacheKey);
            }
        } catch (Exception e) {
            key.clear();
            throw new Exception();
        }
        idList.addAll(key);
    }

    /**
     * 执行发送操作
     *
     * @param idList   存到redis中的key
     * @param entity
     * @param userInfo
     */
    private void executeBatch(List<String> idList, MessageEntity entity, UserInfo userInfo) {
        List<String> toUserIdList = null;
        String cacheKey = null;
        try {
            String dbName = "true".equals(configValueUtil.getMultiTenancy()) ?
                    userProvider.get().getTenantDbConnectionString() : dataSourceUtils.getDbName();
            for (int i = 0; i < idList.size(); i++) {
                @Cleanup Connection conn = ConnUtil.getConn(dataSourceUtils, dbName);
                @Cleanup PreparedStatement pstm = null;
                String sql = "INSERT INTO base_message_receive(ID,MESSAGE_ID,USER_ID,IS_READ,BODY_TEXT)  VALUES (?,?,?,?,?)";
                pstm = conn.prepareStatement(sql);
                conn.setAutoCommit(false);
                cacheKey = idList.get(i);
                List<List<String>> list = (List) redisUtil.get(cacheKey, 0, -1);
                toUserIdList = list.get(0);
                for (String toUserId : toUserIdList) {
                    pstm.setString(1, RandomUtil.uuId());
                    pstm.setString(2, entity.getId());
                    pstm.setString(3, toUserId);
                    pstm.setInt(4, 0);
                    pstm.setString(5, entity.getBodyText());
                    pstm.addBatch();
                }
                long time = System.currentTimeMillis();
                log.info("开始提交事务，时间：" + time);
                pstm.executeBatch();
                conn.commit();
                long endTime = System.currentTimeMillis();
                log.info("事务提交完成：" + endTime + "耗时：" + (endTime - time));
                pstm.close();
                conn.close();
                //消息推送 - PC端
                for (int j = 0; j < toUserIdList.size(); j++) {
                    for (OnlineUserModel item : OnlineUserProvider.getOnlineUserList()) {
                        if (toUserIdList.get(j).equals(item.getUserId()) && userInfo.getTenantId().equals(item.getTenantId())) {
                            JSONObject map = new JSONObject();
                            map.put("method", "messagePush");
                            map.put("unreadNoticeCount", 1);
                            map.put("messageType", 1);
                            map.put("userId", userInfo.getTenantId());
                            map.put("toUserId", toUserIdList);
                            map.put("title", entity.getTitle());
                            synchronized (map) {
                                item.getWebSocket().getAsyncRemote().sendText(map.toJSONString());
                            }
                        }
                    }
                }
                redisUtil.remove(cacheKey);
            }
        } catch (Exception e) {
            log.error(e.getMessage());
            log.error("发送消息发生错误，Key值：" + cacheKey + "，发送人：" + userInfo.getUserId());
        }

    }


    @Override
    public void sentMessage(List<String> toUserIds,Integer type, String title) {
        this.sentMessage(toUserIds,type, title,null, null);
    }

    @Override
    @DSTransactional
    public void sentMessage(List<String> toUserIds,Integer type, String title, String descripts,Map<String, String> bodyText) {
        UserInfo userInfo = userProvider.get();
        MessageEntity entity = new MessageEntity();
        entity.setTitle(title);
        entity.setDescription(descripts);

        entity.setBodyText(JsonUtil.getObjectToString(bodyText));
        entity.setId(RandomUtil.uuId());
        entity.setType(type);
        entity.setCreatorUserId(userInfo.getUserId());
        entity.setCreatorTime(new Date());
        entity.setLastModifyTime(entity.getCreatorTime());
        entity.setLastModifyUserId(entity.getCreatorUserId());
        List<MessageReceiveEntity> receiveEntityList = new ArrayList<>();
        for (String item : toUserIds) {
            MessageReceiveEntity messageReceiveEntity = new MessageReceiveEntity();
            messageReceiveEntity.setId(RandomUtil.uuId());
            messageReceiveEntity.setMessageId(entity.getId());
            messageReceiveEntity.setUserId(item);
            messageReceiveEntity.setIsRead(0);
            String msg = bodyText.get(item) != null ? bodyText.get(item) : "{}";
            messageReceiveEntity.setBodyText(msg);
            receiveEntityList.add(messageReceiveEntity);
        }
        this.save(entity);
        for (MessageReceiveEntity messageReceiveEntity : receiveEntityList) {
            messagereceiveService.save(messageReceiveEntity);
        }
        //消息推送 - PC端
        for (int i = 0; i < toUserIds.size(); i++) {
            for (OnlineUserModel item : OnlineUserProvider.getOnlineUserList()) {
                if (toUserIds.get(i).equals(item.getUserId()) && userInfo.getTenantId().equals(item.getTenantId())) {
                    JSONObject map = new JSONObject();
                    map.put("method", "messagePush");
                    map.put("unreadNoticeCount", 1);
                    map.put("messageType", 2);
                    map.put("userId", userInfo.getTenantId());
                    map.put("toUserId", toUserIds);
                    map.put("title", entity.getTitle());
                    synchronized (map) {
                        item.getWebSocket().getAsyncRemote().sendText(map.toJSONString());
                    }
                }
            }
        }
    }

    @Override
    @DSTransactional
    public void sentMessage(List<String> toUserIds, String title, String bodyText, Map<String, String> contentMsg) {
        UserInfo userInfo = userProvider.get();
        MessageEntity entity = new MessageEntity();
        entity.setTitle(title);
        entity.setBodyText(bodyText);
        entity.setId(RandomUtil.uuId());
        entity.setType(2);
        entity.setEnabledMark(1);
        entity.setCreatorUserId(userInfo.getUserId());
        entity.setCreatorTime(new Date());
        entity.setLastModifyTime(entity.getCreatorTime());
        entity.setLastModifyUserId(entity.getCreatorUserId());
        List<MessageReceiveEntity> receiveEntityList = new ArrayList<>();
        for (String item : toUserIds) {
            MessageReceiveEntity messageReceiveEntity = new MessageReceiveEntity();
            messageReceiveEntity.setId(RandomUtil.uuId());
            messageReceiveEntity.setMessageId(entity.getId());
            messageReceiveEntity.setUserId(item);
            messageReceiveEntity.setIsRead(0);
            String msg = contentMsg.get(item) != null ? contentMsg.get(item) : "{}";
            messageReceiveEntity.setBodyText(msg);
            receiveEntityList.add(messageReceiveEntity);
        }
        this.save(entity);
        for (MessageReceiveEntity messageReceiveEntity : receiveEntityList) {
            messagereceiveService.save(messageReceiveEntity);
        }
        //消息推送 - PC端
        for (int i = 0; i < toUserIds.size(); i++) {
            for (OnlineUserModel item : OnlineUserProvider.getOnlineUserList()) {
                if (toUserIds.get(i).equals(item.getUserId()) && userInfo.getTenantId().equals(item.getTenantId())) {
                    JSONObject map = new JSONObject();
                    map.put("method", "messagePush");
                    map.put("unreadNoticeCount", 1);
                    map.put("messageType", 2);
                    map.put("userId", userInfo.getTenantId());
                    map.put("toUserId", toUserIds);
                    map.put("title", entity.getTitle());
                    synchronized (map) {
                        item.getWebSocket().getAsyncRemote().sendText(map.toJSONString());
                    }
                }
            }
        }
    }

    @Override
    public void sentTemplateMessage(List<String> toUserIds,Integer type, MessageTemplateEntity entity, Map<String, String> parameterMap,Map<String,String> bodyText) {
        if (entity != null) {
            // 消息标题
            String title = entity.getTitle();
            // 替换参数
            if (StringUtils.isNotEmpty(title) && null != parameterMap) {
                StringSubstitutor strSubstitutor = new StringSubstitutor(parameterMap, "{", "}");
                title = strSubstitutor.replace(title);
            }
            // 替换参数
            String content = entity.getContent();
            // 替换参数
            if (StringUtils.isNotEmpty(content) && null != parameterMap) {
                StringSubstitutor strSubstitutor = new StringSubstitutor(parameterMap, "{", "}");
                content = strSubstitutor.replace(content);
            }
            parameterMap.put("title",title);
            parameterMap.put("content" , content);
            this.sentMessage(toUserIds,type, title, content, bodyText);
        }
    }



}
